﻿using System;
using System.Security.Authentication;
using K9Nano.Configuration.Startup;
using K9Nano.MultiTenancy;
using K9Nano.Share;

namespace K9Nano.Runtime
{
    public abstract class SessionContextBase : ISessionContext
    {
        public const string SessionOverrideContextKey = "Session.Override";

        protected readonly IAmbientScopeProvider<SessionOverride> SessionOverrideScopeProvider;

        protected readonly IMultiTenancyConfig MultiTenancyConfig;

        protected SessionContextBase(IMultiTenancyConfig multiTenancyConfig,
            IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider)
        {
            SessionOverrideScopeProvider = sessionOverrideScopeProvider;
            this.MultiTenancyConfig = multiTenancyConfig;
        }

        protected SessionOverride OverridedValue => SessionOverrideScopeProvider.GetValue(SessionOverrideContextKey);

        protected abstract Guid? GetTenantId();

        public virtual Guid? TenantId
        {
            get
            {
                if (!MultiTenancyConfig.IsEnabled)
                {
                    return null;
                }

                if (OverridedValue != null)
                {
                    return OverridedValue.TenantId;
                }
                /*
                 * 仅在DbContext的OnModelCreating中，GlobalFilter中使用了TenantId，所以GetTenantId不能通过DbContext读取数据库
                 */
                return GetTenantId();
            }
        }

        public virtual Guid RequiredTenantId => TenantId ?? throw new AuthenticationException("TenantId is null");

        protected abstract Guid? GetUserId();

        public virtual Guid? UserId
        {
            get
            {
                if (OverridedValue != null)
                {
                    return OverridedValue.UserId;
                }

                return GetUserId();
            }
        }

        public virtual Guid RequiredUserId => UserId ?? throw new AuthenticationException("UserId is null");

        private string _userFullName;

        protected virtual string GetUserFullName() => GetClaim(K9SharedConstants.NameClaimType);

        public virtual string UserFullName => _userFullName ??= GetUserFullName();

        private string _userName;

        protected virtual string GetUserName() => GetClaim(K9SharedConstants.UserNameClaimType);

        public virtual string UserName => _userName ??= GetUserName();

        public virtual EMultiTenancySides MultiTenancySide => MultiTenancyConfig.IsEnabled
                                                              && TenantId.HasValue
                    ? EMultiTenancySides.Tenant
                    : EMultiTenancySides.Host;

        public virtual string RequestId { get; set; }

        private string[] _roles;

        public virtual string[] Roles => _roles ??= (GetRoles() ?? new string[0]);

        protected abstract string[] GetRoles();

        private string[] _scopes;

        public virtual string[] Scopes => _scopes ??= (GetScopes() ?? new string[0]);

        protected abstract string[] GetScopes();

        private string _clientId;

        public virtual string ClientId => _clientId ??= (GetClaim(K9SharedConstants.ClientIdClaimType) ?? string.Empty);

        public IDisposable Use(Guid? tenantId, Guid? userId)
        {
            _roles = null;
            _userFullName = null;
            _userName = null;
            return SessionOverrideScopeProvider
                .BeginScope(SessionOverrideContextKey, new SessionOverride(tenantId, userId));
        }

        public abstract string GetClaim(string claimType);
    }
}